#include "StdAfx.h"
#include "BuilderCSV.h"
#include <iosfwd>

//////////////////////////////////////////////////////////////////////////
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	03/07/2014
//
//This class builds a PPR tree structure from CSV data
//////////////////////////////////////////////////////////////////////////

BuilderCSV::BuilderCSV(void)
{
}

bool BuilderCSV::Is_number(string s)
{
	std::locale loc;
	std::string::const_iterator it = s.begin();
	while (it != s.end() && std::isdigit(*it, loc)) ++it;
	return !s.empty() && it == s.end();
}

//////////////////////////////////////////////////////////////////////////
//Builds PPR branches from input csv rows
//same steps as below but list of string instead of input file
//////////////////////////////////////////////////////////////////////////
hash_map<string, PPR> BuilderCSV::BuildFromCSV(hash_map<string, PPR> branches, list<string> inputRows, string actionCol, vector<string> actions, 
												string observationCol, vector<string> observations, string stateCol, int treeLength, 
												vector<string> states, vector<string> joinStates)
{
	list<PPRObservationAction> pairs = ConvertDataToPairs(inputRows, actionCol, actions, observationCol, observations, joinStates);
	branches = PairsToTrees(branches, pairs, observations, treeLength);

	return branches;
}


//////////////////////////////////////////////////////////////////////////
//Builds a list of PPRs from data in csv files
//
//Algorithm
//1.Convert CSV into a list of objects, each containing observation-action
//	pairs
//2.Loop through t time steps of objects to build a branch
//3.Determine if the branch fits any existing PPR trees, if it does add it
//	to them, if not start a new tree using the branch
//4.Continue until all data is exhausted
//////////////////////////////////////////////////////////////////////////
hash_map<string, PPR> BuilderCSV::BuildFromCSV(hash_map<string, PPR> branches, string inputFile, string actionCol, vector<string> actions, 
												string observationCol, vector<string> observations, string stateCol, int treeLength, 
												vector<string> states, vector<string> joinStates)
{
	//hash_map<string, PPR> trees;

	ifstream csvFile(inputFile.c_str());

	string temp;
	list<string> rows;

	while(getline(csvFile, temp))
	{
		rows.push_back(temp);
	}

	return BuildFromCSV(branches, rows, actionCol, actions, observationCol, observations, stateCol, treeLength, states, joinStates);
}


//////////////////////////////////////////////////////////////////////////
//Reads the input CSV file and converts it to observation action pairs
//////////////////////////////////////////////////////////////////////////
//list<PPRObservationAction> BuilderCSV::ConvertDataToPairs(string inputFile, string actionCol, vector<string> actions, string observationCol, vector<string> observations, string stateCol, vector<string> states)
//{	
//	ifstream csvFile(inputFile.c_str());
//
//	string temp;
//	list<string> rows;
//
//	while(getline(csvFile, temp))
//	{
//		rows.push_back(temp);
//	}
//
//	list<PPRObservationAction> pairs = ConvertDataToPairs(rows, actionCol, actions, observationCol, observations, stateCol, states); 
//
//	return pairs;
//}

//////////////////////////////////////////////////////////////////////////
//Converts csv data from list of strings to pairs same as above method
//but uses list of strings instead of input file
//////////////////////////////////////////////////////////////////////////
list<PPRObservationAction> BuilderCSV::ConvertDataToPairs(list<string> rows, string actionCol, vector<string> actions, string observationCol, 
															vector<string> observations, vector<string> joinStates)
{
	list<PPRObservationAction> pairs;
	string temp;

	if(rows.size() > 0)
	{
		//Get the index of the headings for actions and observations
		string colHeaders = rows.front();
		rows.pop_front();
		int actionIndex = 0;
		int observationindex = 0;	
		
		//int stateIndex = -1;
		list<int> stateIndexes;
		
		int i = 0;
		while (colHeaders.find(",", 0) != string::npos)
		{
			size_t  pos = colHeaders.find(",", 0);
			temp = colHeaders.substr(0, pos);
			colHeaders.erase(0, pos + 1);
			if(temp == actionCol)
			{
				actionIndex = i;
			}
			else if(temp == observationCol)
			{
				observationindex = i;
			}
			else //if(temp == stateCol)
			{
				//stateIndex = i;
				for(vector<string>::iterator it = joinStates.begin(); it != joinStates.end(); it++)
				{
					string joinState = *it;
					if(joinState == temp)
					{
						stateIndexes.push_back(i);
					}
				}				
			}

			i++;
		}

		//Parse each row and select the action and observation from each row
		while(rows.size() > 0)
		{
			string row = rows.front();

			PPRObservationAction pair;

			int i = 0;
			while (row.find(",", 0) != string::npos)
			{
				size_t  pos = row.find(",", 0);
				temp = row.substr(0, pos);
				row.erase(0, pos + 1);

				if(Is_number(temp))
				{
					int index = atoi(temp.c_str());

					if(i == actionIndex)
					{
						pair.action = actions[index];
					}
					else if(i == observationindex)
					{
						pair.observation = observations[index];
					}
					else// if(i == stateIndex)
					{
						/*if(states.size() > 0)
						{
						pair.state = states[index];
						}
						else
						{
						pair.state = temp;
						}*/		

						for(list<int>::iterator it = stateIndexes.begin(); it != stateIndexes.end(); it++)
						{
							int ti = *it;

							if(i == ti)
							{
								stringstream ss;
								//ss << pair.state << states[index];
								ss << pair.state << temp;
								pair.state = ss.str();
							}
						}
					}
				}
				else
				{
					if(i == actionIndex)
					{
						pair.action = temp;
					}
					else if(i == observationindex)
					{
						pair.observation = temp;
					}
					else// if(i == stateIndex)
					{
						//pair.state = temp;	
						for(list<int>::iterator it = stateIndexes.begin(); it != stateIndexes.end(); it++)
						{
							int ti = *it;

							if(i == ti)
							{
								stringstream ss;
								ss << pair.state << temp;
								pair.state = ss.str();
							}
						}
					}
				}
				

				i++;
			}

			pairs.push_back(pair);

			rows.pop_front();
		}
	}	

	return pairs;
}


//////////////////////////////////////////////////////////////////////////
//Groups pairs into sequential groups with the same size as the tree size
//and converts each group into a branch, each branch in the hash_map is
//unique so no repeated branches
//////////////////////////////////////////////////////////////////////////
hash_map<string, PPR> BuilderCSV::PairsToTrees(hash_map<string, PPR> branches, list<PPRObservationAction> pairs, vector<string> observations, int treeLength)
{
	list<string> observationsList;
	copy(observations.begin(), observations.end(), back_inserter(observationsList));
	list<string> empty;

	while(pairs.size() > 0)
	{
		int i = 0;

		ActionNode *rootNode = NULL;
		ActionNode *lastNode = NULL;

		while((i < treeLength) && (pairs.size() > 0))
		{
			PPRObservationAction pair = pairs.front();
			pairs.pop_front();

			if(i == 0)
			{
				rootNode = new ActionNode(pair.action, observationsList, pair.state);
				lastNode = rootNode;
			}
			else if (i < treeLength - 1)
			{
				ActionNode *node = new ActionNode(pair.action, lastNode, observationsList);
				lastNode->AddChildNode(pair.observation, node);
				lastNode = node;
			}
			else
			{
				ActionNode *node = new ActionNode(pair.action, lastNode, empty);
				lastNode->AddChildNode(pair.observation, node);
				lastNode = node;
			}

			i++;
		}	

		//Currently can only merge trees of same length
		if(rootNode->GetChildTreeLength() == treeLength)
		{
			PPR branch(rootNode);// = new PPR(rootNode);
			branches = MergeBranchInWithTrees(branches, branch);
		}		
	}

	return branches;
}

//////////////////////////////////////////////////////////////////////////
//Merges a new PPR in with existing branches using the following steps
//1.If branches.size = 0 then just add and return branches
//2.Else loop through each branch already in branches and check if can
//	be merged
//	2.1. If merging possible with a branch remove the branch from the
//			map and copy both branches
//	2.2. Merge branches
//	2.3. Check that merged branches don t already match one in the hash_map
//			using the ToString function to compare with keys
//	2.4. If no clashes add the newly merged branch to the branches set 
//			and return
//////////////////////////////////////////////////////////////////////////
hash_map<string, PPR> BuilderCSV::MergeBranchInWithTrees(hash_map<string, PPR> branches, PPR branch)
{
	//branches.insert(make_pair(branch.GetRootNode()->ToString(), branch));
	//return branches;

	bool merged = false;
	string tempPPRKey;
	PPR tempPPR;

	for(hash_map<string, PPR>::iterator it = branches.begin(); (it != branches.end()) && (!merged); it++)
	{
		tempPPR = it->second;
		tempPPRKey = it->first;

		if(tempPPR.CanMergeWithBranch(branch))
		{
			tempPPR.MergeWithBranch(branch);	
			merged = true;
		}
	}

	if(!merged)
	{
		branches.insert(make_pair(branch.GetRootNode()->ToString(), branch));
	}
	else
	{
		branches.erase(tempPPRKey);
		branches.insert(make_pair(tempPPR.GetRootNode()->ToString(), tempPPR));
	}

	return branches;

	//Step 1
	//if(branches.size() == 0)
	//{
	//	branches.insert(make_pair(branch.GetRootNode()->ToString(), branch));
	//	return branches;
	//}

	//string eraseKey = "";

	//for(hash_map<string, PPR>::iterator it = branches.begin(); (it != branches.end()) && (eraseKey == ""); it++)
	//{
	//	PPR tempPPR = it->second;
	//	string tempPPRKey = tempPPR.GetRootNode()->ToString();

	//	//If can merge try to merge
	//	if(tempPPR.CanMergeWithBranch(branch))
	//	{
	//		lastMerge last = tempPPR.MergeWithBranch(branch);			
	//		if(branches.find(tempPPR.GetRootNode()->ToString()) == branches.end())
	//		{
	//			eraseKey = tempPPRKey;
	//			//branches.erase(eraseKey);
	//			//branches.insert(make_pair(tempPPR.GetRootNode()->ToString(), tempPPR));
	//		}
	//		else
	//		{
	//			//tempPPR.RevertLastMerge(last);
	//		}
	//	}
	//}

	//if(eraseKey != "")
	//{
	//	hash_map<string, PPR>::iterator it = branches.find(eraseKey);

	//	if(it != branches.end())
	//	{
	//		PPR tempPPR = it->second;
	//		branches.erase(eraseKey);
	//		branches.insert(make_pair(tempPPR.GetRootNode()->ToString(), tempPPR));
	//	}		
	//}
	//else
	//{
	//	//cout << "DONT MERGE" << endl;
	//	////If doesn't already exist then add
	//	if(branches.find(branch.GetRootNode()->ToString()) == branches.end())
	//	{
	//		branches.insert(make_pair(branch.GetRootNode()->ToString(), branch));
	//	}
	//}

	//return branches;
}
